package com.mark.springsecurity.domain;

import com.mark.springsecurity.dto.req.SysUserCmd;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.annotations.Type;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.StringJoiner;

@Slf4j
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Builder
@EntityListeners(AuditingEntityListener.class)
@Table(indexes = @Index(columnList = "realName,username,role"))
public class SysUser implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 50)
    private String realName;

    @Column(unique = true, length = 200)
    private String username;

    @Column(length = 200)
    private String password;

    @Column(length = 50)
    private String role;

    @Column(nullable = false)
    @Type(type = "yes_no")
    private Boolean enabled;

    @ManyToMany(targetEntity = SysRole.class, fetch = FetchType.EAGER)
    private Set<SysRole> roles;

    @CreatedBy
    @Column(nullable = false, length = 50, updatable = false)
    private String createdBy;

    @CreatedDate
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdDate;

    @LastModifiedBy
    @Column(length = 50)
    private String lastModifiedBy;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new HashSet<>();
        roles.forEach(
                part ->
                        part.getAuthorities()
                                .forEach(
                                        authority ->
                                                authorities.add(new SimpleGrantedAuthority(authority.getValue()))));
        return authorities; // 获取当前用户的角色集合，通过角色集合获得当前用户的权限集合。
    }

    public void update(SysUser sysUser) {
        this.id = sysUser.getId();
        this.role = sysUser.getRole();
        this.username = sysUser.getUsername();
        this.realName = sysUser.getRealName();
        this.password = SysUserCmd.passwordEncode(sysUser.getPassword());
        this.roles = sysUser.getRoles();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    public void remove() {
        this.enabled = Boolean.FALSE;
    }

    @Override
    public String toString() {
        return new StringJoiner(", ", SysUser.class.getSimpleName() + "[", "]")
                .add("id=" + id)
                .add("realName='" + realName + "'")
                .add("username='" + username + "'")
                .add("password='" + password + "'")
                .add("role='" + role + "'")
                .add("enabled=" + enabled)
                .add("roles=" + roles)
                .add("createdBy='" + createdBy + "'")
                .add("createdDate=" + createdDate)
                .add("lastModifiedBy='" + lastModifiedBy + "'")
                .add("lastModifiedDate=" + lastModifiedDate)
                .toString();
    }
}
